package kernel.loaders.binary;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import kernel.stl.Facet;
import kernel.stl.STL;
import kernel.stl.Solid;
import kernel.stl.Vector;
import kernel.stl.PointXYZ;

/**
 *
 * @author George
 */
public class BinaryParser {

    private static int HEADER_SIZE = 80;
    private BinaryScanner scanner;

    public BinaryParser(BinaryScanner scanner) {
        this.scanner = scanner;
    }

    private float byteSequenceToFloat(int byte1, int byte2, int byte3, int byte4) {
        int sign = byte1 / 128;

        int exponent = ((byte1 % 128) * 2) + (byte2 / 128) - 127;

        double sum = ((byte2 % 128) * (65536)) + (byte3 * 256) + byte4 + Math.pow(2, 24 - 1);

        double factor = 1;
        double mantisse = 0;

        for (int i = 24; i > 0; i--) {
            if (sum >= Math.pow(2, i - 1)) {
                mantisse = mantisse + factor;
                sum = sum - (Math.pow(2, i - 1));
            }
            factor = factor / 2;
        }

        if (sign == 0) {
            return (float) (Math.pow(mantisse, exponent));
        } else {
            return (float) (Math.pow(mantisse, exponent) * -1);
        }
    }

    private int byteSequenceToUsignedLongInt(int byte1, int byte2, int byte3, int byte4) {
        return (int) (byte4 * Math.pow(2, 24) + byte3 * Math.pow(2, 16) + byte2 * Math.pow(2, 8) + byte1);
    }

    private int byteSequenceToUnsignedInt(int byte1, int byte2) {
        return (int) (byte1 * Math.pow(2, 8) + byte2);

    }

    public STL parseSTL() {
        try {
            Solid solid = new Solid("SolidName");

            scanner.skip(HEADER_SIZE);

            int facetCount = parseFacetCount();
            int i = 0;

            for (i = 0; i < facetCount; i++) {
                Facet facet = parseFacet();
                solid.addFacet(facet);
            }

            return new STL(solid);
        } catch (IOException ex) {
            return null;
        }
    }

    public PointXYZ parseVertex() throws IOException {
        float x = parseFloat();
        float y = parseFloat();
        float z = parseFloat();

        return new PointXYZ(x, y, z);
    }

    public Vector parseVector() throws IOException {
        float x = parseFloat();
        float y = parseFloat();
        float z = parseFloat();

        return new Vector(x, y, z);
    }

    public Vector parseNormal() throws IOException {
        return parseVector();
    }

    public Facet parseFacet() throws IOException {
        Vector normal = parseNormal();
        PointXYZ vertex1 = parseVertex();
        PointXYZ vertex2 = parseVertex();
        PointXYZ vertex3 = parseVertex();

        parseUnsignedInt();

        return new Facet(vertex1, vertex2, vertex3, normal);
    }

    private int parseFacetCount() throws IOException {
        return parseUnsignedLongInt();
    }

    private int parseUnsignedLongInt() throws IOException {
        int firstByte = scanner.getNextByte();
        int secondByte = scanner.getNextByte();
        int thirdByte = scanner.getNextByte();
        int fourthByte = scanner.getNextByte();

        return byteSequenceToUsignedLongInt(firstByte, secondByte, thirdByte, fourthByte);
    }

    private int parseUnsignedInt() throws IOException {
        int firstByte = scanner.getNextByte();
        int secondByte = scanner.getNextByte();

        return byteSequenceToUnsignedInt(firstByte, secondByte);
    }

    private void parseHeader() throws IOException {
        //Cabeçalho formado por 80 bytes não significantes.
        scanner.skip(this.HEADER_SIZE);
    }

    private float parseFloat() throws IOException {
        int firstByte = scanner.getNextByte();
        int secondByte = scanner.getNextByte();
        int thirdByte = scanner.getNextByte();
        int fourthByte = scanner.getNextByte();

        return byteSequenceToFloat(firstByte, secondByte, thirdByte, fourthByte);
    }
}
